#include "my.h"

typedef struct
{
	int type;//请求的类型
	char username[50];//用户名
	char passwd[50];//用户密码
	char filename[50];
	char filedata[100];
	int size;
}data_t;

sqlite3 *db = NULL;//将句柄定义为全局变量

//自定义一个mySqlite3Exec
void mySqlite3Exec(const char *sql)
{
	char *errmsg = NULL;
	int ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
	if(ret != 0)
	{
		printf("sqlite3_exec failed:%s\n",errmsg);
		exit(-1);
	}
	printf("%s成功了!!\n",sql);
}
//自定义一个mySqlite3GetTable
int mySqlite3GetTable(const char *sql)
{
	char **resultp = NULL;
	int row = 0,column,i;
	char *errmsg = NULL;
	int ret = sqlite3_get_table(db, sql, &resultp, &row, &column, &errmsg);
	if(ret != 0)
	{
		printf("sqlite3_get_table failed:%s\n",errmsg);
		exit(-1);
	}

	return row; //将查询结果的行数返回
}

//创建私人的网盘目录
void createDir(data_t s)
{
	pid_t ret = fork();
	if(ret == -1)
	{
		perror("fork failed");
		exit(-1);
	}
	else if(ret == 0)
	{
		char buf[100] = { 0 };
		sprintf(buf, "./share/%s",s.username);// mkdir ./share/zhangsan
		execlp("mkdir","mkdir", buf, NULL);
	}
	else if(ret > 0)
	{
		wait(NULL);//回收子进程的资源，避免僵尸进程
		printf("创建私人网盘%s目录完毕!!\n",s.username);
	}

}

//处理注册请求的函数
void dealRegister(data_t s, int newsockfd)
{
	char sql[200] = { 0 };
	//1.去数据库中的user_info表中去校验，判断用户名是否存在
	sprintf(sql,"select * from user_info where username = '%s'", s.username);
	int row = mySqlite3GetTable(sql);
	//2.对查询结果进行判断
	if(row == 0)//说明用户名在数据库中不存在，允许被注册
	{
		//2.1将用户名和密码保存到数据库user_info表中
		sprintf(sql,"insert into user_info values('%s', '%s')", s.username, s.passwd);
		mySqlite3Exec(sql);
		//2.2用该用户的用户名创建一个文件夹做为该用户的私人网盘
		createDir(s);
		s.type = 0;//用0来代表注册成功
	}
	else//用户名在数据库存在，本次注册失败
	{
		s.type = -1;//用-1来代表注册失败
	}
	//3.将注册结果发送给客户端
	send(newsockfd, &s, sizeof(s), 0);
}

//处理登录请求的函数
void dealLogin(data_t s, int newsockfd)
{
	//1.去数据库中校验用户名和密码
	char sql[200] = { 0 };
	sprintf(sql,"select * from user_info where username = '%s'",s.username);
	int row = mySqlite3GetTable(sql);
	if(row == 0)//说明用户名不存在，错误原因就是用户名不存在
		s.type = -1;//代表登录失败原因是用户名不存在
	else//说明用户名存在，继续判断用户密码是否正确
	{
		sprintf(sql,"select * from user_info where username = '%s' and passwd = '%s'",s.username,s.passwd);
		int row2 = mySqlite3GetTable(sql);
		if(row2 == 0)//说明密码不正确
			s.type = -2;//代表登录失败的原因是密码错误
		else
		{
			s.type = 0;//说明登录成功
		}
	}
	//2.将登录的结果发送给客户端
	send(newsockfd, &s, sizeof(s), 0);
}

void dealgetList(data_t s,int newsockfd)
{
	struct dirent *ep = NULL;
	char pathName[100] = { 0 };
	sprintf(pathName,"./share/%s",s.username);
	DIR *dp = opendir(pathName);
	if(dp == NULL)
	{
		perror("opendir failed");
		return;
	}
	while((ep = readdir(dp)) != NULL)
	{
		if(ep->d_name[0] != '.')
		{
			s.type = 3;
			strcpy(s.filename,ep->d_name);
			send(newsockfd,&s,sizeof(s),0);
		}
	}
	s.type = 0;
	send(newsockfd,&s,sizeof(s),0);
	closedir(dp);
}

void getSystemTime(char *p)
{
	time_t t;
	time(&t);
	struct tm *tp = localtime(&t);
	sprintf(p,"%d-%02d-%02d %02d:%02d:%02d",tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday,tp->tm_hour,tp->tm_min,tp->tm_sec);
}

void dealUpLoad(data_t s,int newsockfd)
{
	char sysTime[50] = { 0 };
	char sql[200] = { 0 };
	static int flag = 1;
	static int fd;
	char pathName[100] = { 0 };
	sprintf(pathName,"./share/%s/%s",s.username,s.filename);
	if(flag)
	{
		flag = 0;
		fd = open(pathName,O_WRONLY|O_CREAT|O_TRUNC,0666);
		if(fd == -1)
		{
			perror("open failed");
			return;
		}
	}
	if(s.size == -1)
	{
		close(fd);
		getSystemTime(sysTime);
		sprintf(sql,"insert into his_info values('%s','%s','upload %s')",s.username,sysTime,s.filename);
		mySqlite3Exec(sql);
		return;
	}
	
	write(fd,s.filedata,s.size);
}

void dealDownLoad(data_t s,int newsockfd)
{
	char sql[200];
	char sysTime[100];
	char pathName[100] = { 0 };
	sprintf(pathName,"./share/%s/%s",s.username,s.filename);
	int fd = open(pathName,O_RDONLY);
	if(fd == -1)
	{
		perror("open failed");
		s.size = -1;
		send(newsockfd,&s,sizeof(s),0);
		return ;
	}
	s.size = 0;
	send(newsockfd,&s,sizeof(s),0);
	while((s.size = read(fd,s.filedata,sizeof(s.filedata))) > 0)
	{
		send(newsockfd,&s,sizeof(s),0);
	}
	s.size = -2;
	send(newsockfd,&s,sizeof(s),0);
	close(fd);
	getSystemTime(sysTime);
	sprintf(sql,"insert into his_info values('%s','%s','download %s')",s.username,sysTime,s.filename);
	mySqlite3Exec(sql);
}

void *dealHistoryList(data_t s,int newsockfd)
{
	char sql[200] = { 0 };
	char **resultp = NULL;
	int row = 0,column,i;
	char *errmsg = NULL;
	sprintf(sql,"select * from his_info where username = '%s'",s.username);
	int ret = sqlite3_get_table(db, sql, &resultp, &row, &column, &errmsg);
	if(ret != 0)
	{
		printf("sqlite3_get_table failed:%s\n",errmsg);
		exit(-1);
	}
	for(i = 0;i < (row+1)*column;i += column)
	{
		sprintf(s.filedata,"%-30s %-30s %-30s\n",resultp[i],resultp[i+1],resultp[i+2]);
		s.type = 0;
		send(newsockfd,&s,sizeof(s),0);
	}
	s.type = -1;
	send(newsockfd,&s,sizeof(s),0);

}


//与客户端交互的线程
void *doClientThread(void *p)
{
	int newsockfd = *(int*)p;
	data_t s = { 0 };//用来保存客户端发送过来的请求
	while(1)
	{
		int ret = recv(newsockfd, &s, sizeof(s), 0);
		if(ret <= 0)
		{
			printf("newsockfd %d对应的客户端断开连接!!\n",newsockfd);
			close(newsockfd);
			return;
		}
		printf("接收到的请求类型:%d 用户名:%s 密码:%s\n",s.type, s.username, s.passwd);
		//判断客户端的请求类型，来做出不同处理
		switch(s.type)
		{
		case 1://注册请求
			dealRegister(s, newsockfd);//处理注册请求的函数
			break;
		case 2://登录请求
			dealLogin(s, newsockfd);//处理登录请求的函数
			break;
		case 3://获取列表请求
			dealgetList(s,newsockfd);
			break;
		case 4://上传文件请求
			dealUpLoad(s,newsockfd);
			break;
		case 5://下载文件请求
			dealDownLoad(s,newsockfd);
			break;
		case 6://获取历史记录请求
			dealHistoryList(s,newsockfd);
			break;
		}
	}
}
int main(int argc, const char *argv[])
{
	pthread_t id;
	int newsockfd;


	//0.打开数据库
	char sql[200] = { 0 };
	int myret = sqlite3_open("./data.db",&db);
	if(myret != 0)
	{
		printf("sqlite3_open failed!!");
		exit(-1);
	}
	//如果user_info表不存在，创建一个user_info表
	sprintf(sql,"create table if not exists user_info (username varchar(20), passwd varchar(20))");
	mySqlite3Exec(sql);
	
	sprintf(sql,"create table if not exists his_info (username varchar(20), time varchar(50),someting varchar(50))");
	mySqlite3Exec(sql);

	//1.创建一个流式套接字
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		perror("socket failed");
		exit(-1);
	}
	//2.绑定自己本机的IP地址和端口号
	struct sockaddr_in myaddr = { 0 };
	myaddr.sin_family = AF_INET;
	myaddr.sin_port = htons(6666);//端口号
	myaddr.sin_addr.s_addr = htonl(INADDR_ANY);//自动获取IP
	int ret = bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr));
	if(ret == -1)
	{
		perror("bind failed");
		exit(-1);
	}
	//3.设置监听
	listen(sockfd, 5);
	
	//4.多线程并发服务器创建
	while(1)
	{
		newsockfd = accept(sockfd, NULL, NULL);
		printf("新的客户端连接成功,对应的newsockfd is %d\n",newsockfd);
		pthread_create(&id, NULL, doClientThread, &newsockfd);
	}
	return 0;
}